<?php
/**
 * Rights installer component class file.
 *
 * @author Christoffer Niska <cniska@live.com>
 * @copyright Copyright &copy; 2010 Christoffer Niska
 * @since 0.9.3
 */
class RInstaller extends CApplicationComponent
{
	const ERROR_NONE=0;
	const ERROR_QUERY_FAILED=1;

	/**
	 * @property array the roles assigned to users implicitly.
	 */
	public $defaultRoles;
	/**
	 * @property string the name of the superuser role.
	 */
	public $superuserName;
	/**
	 * @property string the name of the authenticated role.
	 */
	public $authenticatedName;
	/**
	 * @property string the name of the guest role.
	 */
	public $guestName;
	/**
	 * @property RAuthManager the authorization manager.
	 */
	private $_authManager;
	/**
	 * @property boolean whether Rights is installed.
	 */
	private $_installed;

	/**
	 * @property CDbConnection
	 */
	public $db;

	/**
	 * Initializes the installer.
	 * @throws CException if the authorization manager or the web user
	 * is not configured to use the correct class.
	 */
	public function init()
	{
		parent::init();

		// Make sure the application is configured
		// to use a valid authorization manager.
		$authManager = Yii::app()->getAuthManager();
		if( ($authManager instanceof RDbAuthManager)===false )
		throw new CException(Rights::t('install', 'Application authorization manager must extend the RDbAuthManager class.'));

		// Make sure the application is configured
		// to use a valid web user.
		$user = Yii::app()->getUser();
		if( ($user instanceof RWebUser)===false )
		throw new CException(Rights::t('install', 'Application web user must extend the RWebUser class.'));

		$this->_authManager = $authManager;
		$this->db = $this->_authManager->db;
	}

	/**
	 * Runs the installer.
	 * @param boolean whether to drop tables if they exists.
	 * @return boolean whether the installer ran successfully.
	 */
	public function run()
	{
		// Get the table names.
		$itemTable = $this->_authManager->itemTable;
		$itemChildTable = $this->_authManager->itemChildTable;
		$assignmentTable = $this->_authManager->assignmentTable;
		$rightsTable = $this->_authManager->rightsTable;

		// Fetch the schema.
		$schema = file_get_contents(dirname(__FILE__).'/../data/schema.sql');

		// Correct the table names.
		$schema = strtr($schema, array(
            'AuthItem'=>$itemTable,
            'AuthItemChild'=>$itemChildTable,
            'AuthAssignment'=>$assignmentTable,
            'Rights'=>$rightsTable,
		));

		// Convert the schema into an array of sql queries.
		$schema = preg_split("/;\s*/", trim($schema, ';'));

		// Start transaction
		$txn = $this->db->beginTransaction();

		try
		{
			// Execute each query in the schema.
			foreach( $schema as $sql )
			{
				$command = $this->db->createCommand($sql);
				$command->execute();
			}

			// Insert the necessary roles.
			$roles = $this->getUniqueRoles();
			foreach( $roles as $roleName )
			{
				$sql = "INSERT INTO {$itemTable} (name, type, data)
                    VALUES (:name, :type, :data)";
				$command = $this->db->createCommand($sql);
				$command->bindValue(':name', $roleName);
				$command->bindValue(':type', CAuthItem::TYPE_ROLE);
				$command->bindValue(':data', 'N;');
				$command->execute();
			}

			// Assign the logged in user the superusers role.
			$sql = "INSERT INTO {$assignmentTable} (itemname, userid, data)
                VALUES (:itemname, :userid, :data)";
			$command = $this->db->createCommand($sql);
			$command->bindValue(':itemname', $this->superuserName);
			$command->bindValue(':userid', Yii::app()->getUser()->id);
			$command->bindValue(':data', 'N;');
			$command->execute();

			// All commands executed successfully, commit.
			$txn->commit();
			return self::ERROR_NONE;
		}
		catch( CDbException $e )
		{
			// Something went wrong, rollback.
			$txn->rollback();

			return self::ERROR_QUERY_FAILED;
		}
	}

	/**
	 * Returns a list of unique roles names.
	 * @return array the list of roles.
	 */
	private function getUniqueRoles()
	{
		$roles = CMap::mergeArray($this->defaultRoles, array(
		$this->superuserName,
		$this->authenticatedName,
		$this->guestName,
		));
		return array_unique($roles);
	}

	/**
	 * @return boolean whether Rights is installed.
	 */
	public function getInstalled()
	{
		if( $this->_installed!==null )
		{
			return $this->_installed;
		}
		else
		{
			$schema = array(
                "SELECT COUNT(*) FROM {$this->_authManager->itemTable}",
                "SELECT COUNT(*) FROM {$this->_authManager->itemChildTable}",
                "SELECT COUNT(*) FROM {$this->_authManager->assignmentTable}",
                "SELECT COUNT(*) FROM {$this->_authManager->rightsTable}",
			);

			try
			{
				foreach( $schema as $sql )
				{
					$command = $this->db->createCommand($sql);
					$command->queryScalar();
				}

				$installed = true;
			}
			catch( CDbException $e )
			{
				$installed = false;
			}

			return $this->_installed = $installed;
		}
	}
}
